package com.fresh.bus.chat.business;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.support.collections.DefaultRedisMap;
import org.springframework.data.redis.support.collections.DefaultRedisSet;
import org.springframework.data.redis.support.collections.RedisMap;
import org.springframework.data.redis.support.collections.RedisSet;
import org.springframework.data.redis.support.collections.RedisZSet;
import org.springframework.stereotype.Component;

import com.fresh.bus.canoe.message.Response;
import com.fresh.bus.canoe.session.Session;
import com.fresh.bus.canoe.session.SessionRoleManager;
import com.fresh.bus.chat.ctrl.OpCodes;
import com.fresh.bus.chat.domain.User;
import com.fresh.bus.chat.persist.SystemDao;
import com.fresh.bus.chat.persist.UserDao;
import com.fresh.bus.chat.util.DateUtil;
import com.fresh.bus.chat.util.MessageUtils;

@Component
public class LoginService implements InitializingBean {
	private static final Logger log = LoggerFactory
			.getLogger(LoginService.class);
	private static final Logger userLog = LoggerFactory.getLogger("userLog");
	
	private final StringRedisTemplate template;
	private final RedisMap<String, String> loginMap;

	public RedisSet<String> sessions;

	@Resource
	private ScheduleManager scheduleManager;
	@Resource
	private TalkService talkService;
	@Resource
	protected SessionRoleManager roleManager;
	@Resource
	private UserService userService;
	@Resource
	private UserDao dao;
	@Resource
	private SystemDao sysDao;

	@Autowired
	public LoginService(StringRedisTemplate template) {
		this.template = template;
		loginMap = new DefaultRedisMap<String, String>(KeyUtils.logins(),
				template);
		sessions = new DefaultRedisSet<String>(KeyUtils.sessions(), template);
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		// TODO Auto-generated method stub

	}

	public String findUid(String login) {
		return loginMap.get(login);
	}

	public String setUid(String login, String id) {
		return loginMap.put(login, id);
	}

	public String removeUid(String login) {
		return loginMap.remove(login);
	}

	public void updateUserAccount(Map<String, Object> map) throws Exception {
		String loginId = (String) map.get("id");
		Map<String, Object> ua = dao.getUserAccount(loginId);
		ua.put("src", map.get("src"));
		ua.put("account", map.get("account"));
		setThirdParyAccount(ua);
		dao.updateUserAccount(loginId, ua);
	}

	public void initUserData(Session session, Map<String, Object> data)
			throws Exception {
		/*
		 * String uid = session.getRole().getId(); BoundHashOperations<String,
		 * Object, Object> o = template .boundHashOps(KeyUtils.uid(uid));
		 * o.put("sex", data.get("sex")); o.put("selfDesc",
		 * data.get("selfDesc")); synchroUser(session);
		 */
		data.put("isFirst", "true");
		data.put("canShare", "true");
		data.put("receiveFeiji", "");
		data.put("sendFeiji", "");
		data.put("maxHeight", "0");
		data.put("level", "0");

		String loginId = (String) data.get("id");
		String id = String.valueOf(dao.addUser(data));
	}

	public void addUser(Map<String, Object> map) throws Exception {
		String loginId = (String) map.get("id");
		Map<String, Object> dbUser = dao.getUserByLoginId(loginId);

		if (dbUser == null) {
			setThirdParyAccount(map);
			// dao.addUser(loginId, password, map);
		} else {
			throw new DuplicateKeyException(loginId);
		}
		/*
		 * users.addFirst(name); return addAuth(name);
		 */
	}

	private void setThirdParyAccount(Map<String, Object> map) {
		String account = (String) map.get("account");
		String src = (String) map.get("src");

		if (src.equals("360")) {
			map.put("360_id", account);
		} else if (src.equals("renren")) {
			map.put("renren_id", account);
		} else if (src.equals("sina")) {
			map.put("sina_id", account);
		} else if (src.equals("qq")) {
			map.put("qq_id", account);
		}
	}

	public boolean auth(String user, String pass) throws Exception {
		try {
			dao.loginUser(user, pass);
		} catch (EmptyResultDataAccessException e) {
			return false;
		}
		return true;
	}

	public String auth360(String user) throws Exception {
		try {
			return dao.login360(user);
		} catch (EmptyResultDataAccessException e) {
			return null;
		}
	}

	public String authQQ(String user) throws Exception {
		try {
			return dao.loginQQ(user);
		} catch (EmptyResultDataAccessException e) {
			return null;
		}
	}

	public String authRenRen(String user) throws Exception {
		try {
			return dao.loginRenRen(user);
		} catch (EmptyResultDataAccessException e) {
			return null;
		}
	}

	public String authSina(String user) throws Exception {
		try {
			return dao.loginSina(user);
		} catch (EmptyResultDataAccessException e) {
			return null;
		}
	}

	public String addAuth(String name) {
		String uid = findUid(name);
		// add random auth key relation
		String auth = UUID.randomUUID().toString();
		loginMap.put(KeyUtils.auth(uid), auth);
		loginMap.put(KeyUtils.authKey(auth), uid);
		return auth;
	}

	public void deleteAuth(String user) {
		String uid = findUid(user);
		String authKey = KeyUtils.auth(uid);
		String auth = loginMap.get(authKey);
		template.delete(Arrays.asList(authKey, KeyUtils.authKey(auth)));
	}

	public void synLatestUserList(Map<String, String> map) {
		String userId = map.get("id");
		int level = Integer.valueOf(map.get("level"));

		if (level > 0) {
			String sex = map.get("sex");
			String province = userService.getProvince(map);
			double score = this.userService.countUserScore(map);
			RedisZSet<String> areaGroup = this.userService.areaGroup(province, sex);
			
			areaGroup.add(userId, score);
			if (areaGroup.size() > 10000) {
				String rUid = areaGroup.first();
				areaGroup.remove(rUid);
				userService.clear(rUid);
			}
		}
	}

	public void setSession(String login, Session session) throws Exception {
		String id = findUid(login);
		Map<String, String> map = null;
		long now = System.currentTimeMillis();
		if (id == null) {
			Map<String, Object> data = dao.getUserByLoginId(login);
			map = userService.readUser(data);
			id = map.get("id");
		} else {
			map = userService.findUser(id);
		}
		synLatestUserList(map);

		if (map != null) {
			User user = new User(id);
			session.setRole(user);
			sessions.add(id);

			session.setAttribute("loginTime", now);
			session.setAttribute("uid", id);
			String sid = map.get("stop");
			session.setAttribute("sid", sid);

			loginInit(id);

			// 通知关注者
			notifyFollower(id, true);
			//记录登录日志
			boolean isNew = map.get("isFirst")==null;
			MDC.put("actionDesc", id+","+ isNew);  
			userLog.info(String.valueOf(OpCodes.ENTRY));  			
		} else {
			throw new Exception("Cannot found user " + login);
		}
	}

	// 登录数据初始化
	private void loginInit(String id) {
		String uid = KeyUtils.uid(id);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(uid);
		Date today = new Date();
		Date latestDay = DateUtil.convertStringToDate(ops.get("latestVisit"));
		int offsetDay = DateUtil.getDaysBetween(latestDay, today);
		ops.put("online", "true");
		String isFirst = ops.get("isFirst");
		// 每日登录初始化
		if (offsetDay >= 1 || isFirst != null) {
			int i = MessageUtils.getWeather();
			ops.put("weather", String.valueOf(i));
			// ops.put("waterInfo", "[0,0,0,0,0]");
			ops.put("canShare", "true");
			ops.put("permitQQNo", String.valueOf(UserService.DAILY_QQNO_AMT));
			ops.put("permitWord", String.valueOf(UserService.DAILY_WORD_AMT));
			ops.put("energy", "100");
			ops.put("permitFeiji", String.valueOf(UserService.DAILY_FEIJI_AMT));
		}
	}

	private void notifyFollower(String id, boolean status) {
		List<Map<String, Object>> list = dao
				.findFansByUser(Integer.valueOf(id));
		String fid;
		Session session;
		Response res;
		Map<String, String> user;

		for (Map<String, Object> m : list) {
			fid = String.valueOf(m.get("id"));
			session = this.roleManager.getSession(fid);
			if (session != null) {
				res = new Response(OpCodes.SWITCH_ONLINE);
				user = userService.findUser(id);
				res.setData(user);
				session.send(res);
			}
		}
	}

	public void offSession(Session session) throws Exception {
		String id = (String) session.getAttribute("uid");
		this.sessions.remove(id);

		if (session.getAttribute("isTalking") != null) {
			talkService.endTalk(session, false);
		}
		if (session.getAttribute("isSelecting") != null) {
			String rid = (String) session.getAttribute("rid");
			scheduleManager.cancelElection(id, rid);
		}
		if (session.getAttribute("rid") != null) {
			scheduleManager.leftQueue(session);
		}

		String uid = KeyUtils.uid(id);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(uid);
		ops.delete("online");
		int loginTimes = Integer.valueOf(ops.get("loginTimes"));
		loginTimes++;
		ops.put("loginTimes", String.valueOf(loginTimes));

		long loginTime = (Long) session.getAttribute("loginTime");
		long leaveTime = System.currentTimeMillis();
		int stayLen = (int) ((leaveTime - loginTime) / (1000 * 60));
		
		ops.put("latestVisit", DateUtil.convertDateToString(new Date()));
		ops.put("stayLen", String.valueOf(stayLen));
		
		String isFirst = ops.get("isFirst");
		if (isFirst != null)ops.delete("isFirst");
		userService.logoutUser(ops.entries());

		// 通知关注者
		notifyFollower(id, false);
		// feijiService.saveMyPost(id);

		// 清除好友关系
		Set<String> asso = template.keys(KeyUtils.asso(id, "*"));
		if (!asso.isEmpty()) {
			template.delete(asso);
		}
	}

}
